/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Driver Module                                  */
/*                                                                           */
/*    Copyright (C) 2007 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    drvDCF77Clock.c - DCF77 Clock based system timer and real time clock   */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <avr/io.h>
#include <avr/interrupt.h>
#include "krnlTypes.h"

///////////////////////////////////////////////////////////////////////////////
// Constants
#define WAITING_FOR_START_BIT 0xff

///////////////////////////////////////////////////////////////////////////////
// Module local variables
static dosWord l_system_time = 0;
static dosByte l_bit_average_counter = 0;
static dosByte l_bit_average = 0;
static dosByte l_bit_counter = WAITING_FOR_START_BIT;
static dosByte l_quanta_counter = 0;
static dosDWord l_time_buffer = 0;
static dosDWord l_date_buffer = 0;

///////////////////////////////////////////////////////////////////////////////
// Init DCF77 system timer and real time clock
void krnlInitSystemTimer(void)
{
	// Init timer0
	TCCR0 = _BV(CS01) | _BV(CS00);
	TIMSK = _BV(TOIE0);
}

///////////////////////////////////////////////////////////////////////////////
// Interrupt handler
SIGNAL(SIG_OVERFLOW0)
{
	dosBool input;

	// set 1ms period
	TCNT0 = 256 - 125;

	l_system_time++;

	// calculate bit average for 10ms
	if( l_bit_average_counter < 10 )
	{
		if( DCF77IsSet() )
			l_bit_average++;

		l_bit_average_counter++;
	}
	else
	{
		// get input state
		input = (l_bit_average > 4);

		// increment time quanta (10ms) counter
		l_quanta_counter++;

		// check bit counter
		if( l_bit_counter == WAITING_FOR_START_BIT )
		{
			// check if input is high
			if( input )
			{
				// if at least 1.7s ellapsed this is the start bit
				if( l_quanta_counter > 170 )
				{
					l_bit_counter = 0;

					// clear buffers
					l_time_buffer = 0;
					l_date_buffer = 0;
				}

				l_quanta_counter = 0;
			}
		}
		else
		{
			// over 1200ms
			if( l_quanta_counter > 120 )
			{
				// no rising edge received restart reception
				l_bit_counter = WAITING_FOR_START_BIT;
				l_quanta_counter = 0;
			}
			else
			{
				// between 800-1200 ms
				if( l_quanta_counter > 80 )
				{
					// if next bit rising edge
					if( input )
					{
						l_bit_counter++;
						l_quanta_counter = 0;

						// if frame received process it and start reception of the next frame
						if( l_bit_counter == 59 )
						{
							// start reception of new frame
							l_bit_counter = WAITING_FOR_START_BIT;
						}
					}
				}
				else
				{
					// 250ms-800ms
					if( l_quanta_counter > 25 )
					{
						// if input is not 0 then restart reception
						if( input )
						{
							l_bit_counter = WAITING_FOR_START_BIT;
							l_quanta_counter = 0;
						}
					}
					else
					{
						// at 150ms
						if( l_quanta_counter == 15 )
						{
							// receive and store bit
							if( input )
							{
								if( l_bit_counter > 15 )
								{
									if( l_bit_counter < 38 )
									{
										l_time_buffer |= 1 << (l_bit_counter-15);
									}
									else
									{
										l_date_buffer |= 1 << (l_bit_counter-38);
									}
								}
							}
						}
						else
						{
							// first 70ms
							if( l_quanta_counter < 7 )
							{
								// if input is not 1 then restart reception
								if( !input )
								{
									l_bit_counter = WAITING_FOR_START_BIT;
									l_quanta_counter = 0;
								}
							}
						}
					}
				}
			}
		}

		// reset average
		l_bit_average = 0;
		l_bit_average_counter = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Get system timer
dosWord krnlGetSystemTimer(void)
{
	return l_system_time;
}
